#include "ClientChannel.h"
#include "Selector.h"

#include <assert.h>
#include <iostream>

namespace nio {

	ClientChannel::ClientChannel(ChannelDelegate* delegate) {
		this->delegate = delegate;
		this->eventLoop = new EventLoop();
		this->eventLoop->startup();
	}

	ClientChannel::ClientChannel(ChannelDelegate* delegate, EventLoop* eventLoop, Socket* socket) {
		this->setStatus(ChannelStatus::connected);
		this->delegate = delegate;
		this->eventLoop = eventLoop;
		this->socket = socket;
	}

	ClientChannel::~ClientChannel() {
		//std::cout << "ClientChannel destroy" << std::endl;
	}

	void ClientChannel::connect(std::string host, int port, int64_t timeout) {
		eventLoop->execute([this, host, port, timeout]() {
			if (this->socket != nullptr) {
				assert(false);
				return;
			}

			this->resolver = std::make_shared<AddressResolver>(host, port);

			this->setStatus(ChannelStatus::connecting);

			try {
				auto addresses = this->resolver->getAddresses();
				auto address = addresses[0];
				auto family = address.getFamily();

				this->socket = new Socket(family, SocketType::stream);
				this->socket->SetNonblocking(true);

				this->eventLoop->selector.addEvents(this, EventSetRead | EventSetWrite);
				if (family == SocketFamily::inet) {
					this->socket->Connect((sockaddr*)&address.storage, sizeof(sockaddr_in));
				}
				else {
					this->socket->Connect((sockaddr*)&address.storage, sizeof(sockaddr_in6));
				}

				this->connectTimeout = this->eventLoop->execute(timeout, false, [this]() {
					SocketError err("Connect timeout", 0);
					this->onDisconnect(&err);
					});
			}
			catch (SocketError& err) {
				this->onDisconnect(&err);
			}
			});
	}

	void ClientChannel::disconnect() {
		eventLoop->execute([this]() {
			if (this->socket == nullptr) {
				assert(false);
				return;
			}
			this->onDisconnect(nullptr);
			});
	}

	void ClientChannel::write(std::string& data) {
		eventLoop->execute([this, data]() {
			if (this->socket == nullptr) {
				assert(false);
				return;
			}
			this->writeBuf += data;
			this->addWritable();
			});
	}

	SocketAddress ClientChannel::getLocalAddress() {
		if (socket == nullptr)return SocketAddress();
		return socket->getLocalAddress();
	}

	SocketAddress ClientChannel::getRemoteAddress() {
		if (socket == nullptr)return SocketAddress();
		return socket->getRemoteAddress();
	}

	SOCKET ClientChannel::getFD() {
		if (socket == nullptr)return INVALID_FD;
		return socket->getfd();
	}

	void ClientChannel::onEvents(int events) {
		if (status == ChannelStatus::connecting) {
			if (events == EventSetWrite) {
				onConnect();
				return;
			}
			else if (events & EventSetWrite && events & EventSetRead) {
				SocketError err("Connect error", Errno);
				onDisconnect(&err);
				return;
			}
		}

		if (events & EventSetWrite) {
			onWritable();
		}

		if (events & EventSetRead) {
			onReadable();
		}
	}


	void ClientChannel::onConnect() {
		setStatus(ChannelStatus::connected);
		if (connectTimeout != nullptr) {
			connectTimeout->Cancel();
			connectTimeout = nullptr;
		}

		removeWritable();
		if (delegate != nullptr)
			delegate->onConnect(this, resolver->host, resolver->port);
	}

	void ClientChannel::onDisconnect(SocketError* error) {
		setStatus(ChannelStatus::idle);
		if (connectTimeout != nullptr) {
			connectTimeout->Cancel();
			connectTimeout = nullptr;
		}
		eventLoop->selector.clearEvents(this);
		if (socket != nullptr) {
			socket->Close();
			delete socket;
			socket = nullptr;
		}
		writeBuf.clear();
		resolver = nullptr;

		if (delegate != nullptr)
			delegate->onDisconnect(this, error);
	}

	void ClientChannel::onWritable() {
		eventLoop->assertCurrentLoop();
		if (writeBuf.size() == 0) {
			removeWritable();
			return;
		}
		if (socket == nullptr) {
			removeWritable();
			return;
		}

		int ret = socket->Send((char*)writeBuf.c_str(), writeBuf.size());
		if (ret == writeBuf.size()) {
			writeBuf.clear();
			removeWritable();
		}
		else if (ret == 0) {
			SocketError err("Peer party disconnected", 0);
			onDisconnect(&err);
		}
		else if (ret > 0) {
			writeBuf.erase(0, ret);
		}
		else if (ret < 0 && !Utils::igorable(Errno)) {
			SocketError err("ClientChannel write error", Errno);
			onDisconnect(&err);
		}
	}

	void ClientChannel::onReadable() {
		eventLoop->assertCurrentLoop();
		if (socket == nullptr)return;

		int size = 1024;
		char* buf = new char[size];
		int ret = socket->Recv(buf, size);
		if (ret > 0) {
			std::string data(buf, ret);
			if (delegate != nullptr)
				delegate->onRead(this, data);
		}
		else if (ret == 0) {
			SocketError err("Peer party disconnected", 0);
			onDisconnect(&err);
		}
		else if (ret < 0 && !Utils::igorable(Errno)) {
			SocketError err("ClientChannel read error", Errno);
			onDisconnect(&err);
		}
		delete[] buf;
	}

	void ClientChannel::addWritable() {
		eventLoop->assertCurrentLoop();
		eventLoop->selector.addEvents(this, EventSetWrite);
	}

	void ClientChannel::removeWritable() {
		eventLoop->assertCurrentLoop();
		eventLoop->selector.removeEvents(this, EventSetWrite);
	}


}